package com.guet.service.impl;

import cn.hutool.core.util.StrUtil;
import com.github.pagehelper.PageHelper;
import com.guet.domain.Shop;
import com.guet.mapper.ShopMapper;
import com.guet.service.ShopService;
import com.guet.utils.RedisUtils;
import com.guet.utils.Result;
import com.guet.utils.SystemConstants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.*;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;

import static com.guet.utils.RedisConstants.*;


@Service
@Slf4j
public class ShopServiceImpl implements ShopService {

    @Autowired
    private ShopMapper shopMapper;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private RedisUtils redisUtils;

    @Override
    public Result getById(Long id) {
        // 缓存穿透
        Shop shop = redisUtils
                .queryWithPassThrough(CACHE_SHOP_KEY,id,Shop.class,shopMapper::getById,CACHE_SHOP_TTL,TimeUnit.MINUTES);

        // 缓存击穿
        //Shop shop = redisUtils
        //        .queryWithLogicalExpire(CACHE_SHOP_KEY,id,Shop.class,shopMapper::getById,CACHE_SHOP_TTL,TimeUnit.MINUTES);


        return shop == null ? Result.fail("店铺不存在"):Result.ok(shop);
    }

    @Override
    public void save(Shop shop) {
        shopMapper.save(shop);
    }

    @Override
    public List<Shop> getByType(Integer typeId, Integer page, Double x, Double y) {
        if(x == null || y == null){
            PageHelper.startPage(page, SystemConstants.DEFAULT_PAGE_SIZE);
            return shopMapper.getByType(typeId);
        }

        // 计算分页,offset后,取(count-offset)个元素,即5个元素
        int offset = (page - 1) * SystemConstants.DEFAULT_PAGE_SIZE;
        int count = page * SystemConstants.DEFAULT_PAGE_SIZE;

        // (x,y) 5公里内的店铺(按距离升序)
        String key = SHOP_GEO_KEY + typeId;
        GeoResults<RedisGeoCommands.GeoLocation<String>> results =
                stringRedisTemplate.opsForGeo().radius(
                        key,
                        new Circle(
                                new Point(x, y),
                                new Distance(5000, RedisGeoCommands.DistanceUnit.METERS)
                        ),
                        RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs()
                                .includeDistance().limit(count)
                );
        if(results == null){
            return Collections.emptyList();
        }
        List<GeoResult<RedisGeoCommands.GeoLocation<String>>> list = results.getContent();
        if(list.size() <= offset){
            // 没有下一页了
            return Collections.emptyList();
        }

        List<Long> ids = new ArrayList<>(list.size());
        Map<String, Distance> distanceMap = new HashMap<>();
        list.stream().skip(offset).forEach(result -> {
            String shopId = result.getContent().getName();
            Distance distance = result.getDistance();
            ids.add(Long.valueOf(shopId));
            distanceMap.put(shopId,distance);
        });

        List<Shop> shops = shopMapper.getByIds(ids);
        for (Shop shop : shops) {
            shop.setDistance(distanceMap.get(shop.getId().toString()).getValue());
        }
        return shops;
    }

    @Override
    public List<Shop> getByNameLike(String name, Integer page) {
        PageHelper.startPage(page,SystemConstants.MAX_PAGE_SIZE);
        List<Shop> list;
        if(StrUtil.isNotBlank(name)){
            list = shopMapper.getByNameLike(name);
        } else {
            list = shopMapper.getAll();
        }
        return list;
    }

    @Override
    @Transactional
    public Result updateById(Shop shop) {
        Long id = shop.getId();
        if(id == null){
            return Result.fail("店铺id不能为空");
        }
        // 先更新数据库
        shopMapper.updateById(shop);
        // 再删除缓存
        stringRedisTemplate.delete(CACHE_SHOP_KEY + id);
        return Result.ok();
    }
}
